home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / kernel / help.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  51.0 KB  |  1,861 lines  |  [TEXT/R*ch]

  1. /* Help support for Xconq.
  2.    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. /* This is basically support code for interfaces, which handle the
  11.    actual help interaction themselves. */
  12.  
  13. /* This file must also be translated (mostly) for non-English Xconq. */
  14.  
  15. #include "conq.h"
  16. extern void notify_instructions PARAMS ((void));
  17.  
  18. static void describe_help_system PARAMS ((int arg, char *key, char *buf));
  19. static void describe_synth_run PARAMS ((char *buf, int methkey));
  20. static int histogram_compare PARAMS ((const void *h1, const void *h2));
  21.  
  22. /* The first help node in the chain. */
  23.  
  24. HelpNode *first_help_node;
  25.  
  26. /* The last help node. */
  27.  
  28. HelpNode *last_help_node;
  29.  
  30. /* The help node with copying and copyright info. */
  31.  
  32. HelpNode *copying_help_node;
  33.  
  34. /* The help node with (non-)warranty info. */
  35.  
  36. HelpNode *warranty_help_node;
  37.  
  38. HelpNode *default_prev_help_node;
  39.  
  40. /* Create the initial help node and link it to itself.  Subsequent nodes will be
  41.    inserted later, after a game has been loaded. */
  42.  
  43. void
  44. init_help()
  45. {
  46.     /* Note that we can't use add_help_node to set up the first help node. */
  47.     first_help_node = create_help_node();
  48.     first_help_node->key = "help system";
  49.     first_help_node->fn = describe_help_system;
  50.     first_help_node->prev = first_help_node->next = first_help_node;
  51.     last_help_node = first_help_node;
  52.     copying_help_node = add_help_node("copyright", describe_copyright, 0, first_help_node);
  53.     warranty_help_node = add_help_node("warranty", describe_warranty, 0, copying_help_node);
  54.     /* Set the place for new nodes to appear normally. */
  55.     default_prev_help_node = copying_help_node;
  56.     add_help_node("news", describe_news, 0, NULL);
  57. }
  58.  
  59. /* This function creates the actual set of help nodes for the kernel. */
  60.  
  61. void
  62. create_game_help_nodes()
  63. {
  64.     int u, m, t;
  65.     char *name, *longname;
  66.     HelpNode *node;
  67.  
  68.     add_help_node("game design", describe_game_design, 0, NULL);
  69.     add_help_node("modules", describe_game_modules, 0, NULL);
  70.     add_help_node("scoring", describe_scorekeepers, 0, NULL);
  71.     add_help_node("game setup", describe_setup, 0, NULL);
  72.     for_all_unit_types(u) {
  73.     longname = u_long_name(u);
  74.     if (!empty_string(longname)) {
  75.         sprintf(spbuf, "%s (%s)", longname, u_type_name(u));
  76.         name = copy_string(spbuf);
  77.     } else {
  78.         name = u_type_name(u);
  79.     }
  80.     node = add_help_node(name, describe_utype, u, NULL);
  81.     node->nclass = utypenode;
  82.     }
  83.     for_all_material_types(m) {
  84.     node = add_help_node(m_type_name(m), describe_mtype, m, NULL);
  85.     node->nclass = mtypenode;
  86.     }
  87.     for_all_terrain_types(t) {
  88.     node = add_help_node(t_type_name(t), describe_ttype, t, NULL);
  89.     node->nclass = ttypenode;
  90.     }
  91.     add_help_node("general concepts", describe_concepts, 0, NULL);
  92.     /* Invalidate any existing topics node. */
  93.     first_help_node->text = NULL;
  94. }
  95.  
  96. /* Create an empty help node. */
  97.  
  98. HelpNode *
  99. create_help_node()
  100. {
  101.     HelpNode *node = (HelpNode *) xmalloc(sizeof(HelpNode));
  102.  
  103.     node->key = NULL;
  104.     node->fn = NULL;
  105.     node->nclass = miscnode;
  106.     node->arg = 0;
  107.     node->text = NULL;
  108.     node->prev = node->next = NULL;
  109.     return node;
  110. }
  111.  
  112. /* Add a help node after the given node. */
  113.  
  114. HelpNode *
  115. add_help_node(key, fn, arg, prevnode)
  116. char *key;
  117. void (*fn)();
  118. int arg;
  119. HelpNode *prevnode;
  120. {
  121.     HelpNode *node, *nextnode;
  122.  
  123.     if (empty_string(key))
  124.       run_error("empty help key");
  125.     node = create_help_node();
  126.     node->key = key;
  127.     node->fn = fn;
  128.     node->arg = arg;
  129.     if (prevnode == NULL)
  130.       prevnode = default_prev_help_node->prev;
  131.     nextnode = prevnode->next;
  132.     node->prev = prevnode;
  133.     node->next = nextnode;
  134.     prevnode->next = node;
  135.     nextnode->prev = node;
  136.     /* Might need to fix last help node. */
  137.     last_help_node = first_help_node->prev;
  138.     return node;
  139. }
  140.  
  141. /* Given a string and node, find the next node whose key matches. */
  142.  
  143. HelpNode *
  144. find_help_node(node, str)
  145. HelpNode *node;
  146. char *str;
  147. {
  148.     HelpNode *tmp;
  149.  
  150.     /* Note that the search wraps around. */
  151.     for (tmp = node->next; tmp != node; tmp = tmp->next) {
  152.         if (strcmp(tmp->key, str) == 0)
  153.           return tmp;
  154.         if (strstr(tmp->key, str) != NULL)
  155.           return tmp;
  156.     }
  157.     return NULL;
  158. }
  159.  
  160. /* Return the string containing the text of the help node, possibly
  161.    computing it first. */
  162.  
  163. char *
  164. get_help_text(node)
  165. HelpNode *node;
  166. {
  167.     int allocsize;
  168.  
  169.     if (node != NULL) {
  170.     /* Maybe calculate the text to display. */
  171.     if (node->text == NULL) {
  172.         if (node->fn != NULL) {
  173.         /* (should allow for variable-size allocation) */
  174.         allocsize = 5000;
  175.         if (node->fn == describe_copyright)
  176.           allocsize = 30000;
  177.             node->text = xmalloc(allocsize);
  178.         if (node->text != NULL) {
  179.             /* Make buffer into an empty string, so that the
  180.                description function can just start appending. */
  181.             (node->text)[0] = '\0';
  182.             node->textend = 0;
  183.             node->textsize = allocsize;
  184.             (*(node->fn))(node->arg, node->key, node->text);
  185.             node->textend = strlen(node->text);
  186.         } else {
  187.             /* Ran out of memory... (would never get here though!) */
  188.         }
  189.         } else {
  190.         /* Generate a default message if nothing to compute help. */
  191.         sprintf(spbuf, "%s: No info available.", node->key);
  192.         node->text = copy_string(spbuf);
  193.         node->textend = strlen(node->text);
  194.         }
  195.     }
  196.     return node->text;
  197.     } else {
  198.     return NULL;
  199.     }
  200. }
  201.  
  202. static void
  203. describe_help_system(arg, key, buf)
  204. int arg;
  205. char *key, *buf;
  206. {
  207.     strcat(buf, "This is the header node of the Xconq help system.\n");
  208.     strcat(buf, "Go forward or backward from here to see the online help.\n");
  209. }
  210.  
  211. /* Create a raw list of help topics by just iterating through all the nodes,
  212.    except for the topics node itself. */
  213.  
  214. void
  215. describe_topics(arg, key, buf)
  216. int arg;
  217. char *key, *buf;
  218. {
  219.     HelpNode *topics, *tmp;
  220.  
  221.     topics = find_help_node(first_help_node, "topics");
  222.     /* Unlikely that we'll call this without the topics node existing
  223.        already, but just in case... */
  224.     if (topics == NULL)
  225.       return;
  226.     for (tmp = topics->next; tmp != topics; tmp = tmp->next) {
  227.     tprintf(buf, "%s", tmp->key);
  228.     tprintf(buf, "\n");
  229.     }
  230. }
  231.  
  232. /* Get the news file and put it into text buffer. */
  233.  
  234. void
  235. describe_news(arg, key, buf)
  236. int arg;
  237. char *key, *buf;
  238. {
  239.     FILE *fp;
  240.  
  241.     fp = fopen(news_filename(), "r");
  242.     if (fp != NULL) {
  243.     tprintf(buf, "XCONQ NEWS\n\n");
  244.     while (fgets(spbuf, BUFSIZE-1, fp) != NULL) {
  245.         tprintf(buf, "%s", spbuf);
  246.     }
  247.     fclose(fp);
  248.     } else {
  249.     tprintf(buf, "(no news)");
  250.     }
  251. }
  252.  
  253. void
  254. describe_concepts(arg, key, buf)
  255. int arg;
  256. char *key, *buf;
  257. {
  258.     tprintf(buf, "Hit points (hp) represent the overall condition of ");
  259.     tprintf(buf, "the unit.");
  260.     tprintf(buf, "\n");
  261.     tprintf(buf, "Action points (acp) are what a unit needs to be able ");
  262.     tprintf(buf, "to do anything at all.");
  263.     tprintf(buf, "\n");
  264. }
  265.  
  266. /* Spit out all the general game_design parameters in a readable fashion. */
  267.  
  268. void
  269. describe_game_design(arg, key, buf)
  270. int arg;
  271. char *key, *buf;
  272. {
  273.     int u, m, t;
  274.     
  275.     /* Replicate title and blurb? (should put title at head of windows, and pages if printed) */
  276.     tprintf(buf, "This game includes %d unit types and %d terrain types",
  277.         numutypes, numttypes);
  278.     if (nummtypes > 0) {
  279.     tprintf(buf, ", along with %d material types", nummtypes);
  280.     }
  281.     tprintf(buf, ".\n");
  282.     if (g_sides_min() == g_sides_max()) {
  283.         tprintf(buf, "Exactly %d sides may play.\n", g_sides_min());
  284.     } else {
  285.         tprintf(buf, "From %d up to %d sides may play.\n", g_sides_min(), g_sides_max());
  286.     }
  287.     tprintf(buf, "\n");
  288.     tprintf(buf, "Player advantages may range from %d to %d, defaulting to %d.\n",
  289.         g_advantage_min(), g_advantage_max(), g_advantage_default());
  290.     tprintf(buf, "\n");
  291.     if (g_see_all()) {
  292.     tprintf(buf, "Everything is always seen by all sides.\n");
  293.     } else {
  294.         if (g_see_terrain_always()) {
  295.         tprintf(buf, "Terrain view is always accurate once seen.\n");
  296.         }
  297.         if (g_see_weather_always()) {
  298.         tprintf(buf, "Weather view is always accurate once terrain seen.\n");
  299.         }
  300.         if (g_terrain_seen()) {
  301.         tprintf(buf, "World terrain is already seen by all sides.\n");
  302.         }
  303.     }
  304.     tprintf(buf, "\n");
  305.     if (g_last_turn() < 9999) {
  306.     tprintf(buf, "Game can go for up to %d turns", g_last_turn());
  307.     if (g_extra_turn() > 0) {
  308.         tprintf(buf, ", with %d%% chance of additional turn thereafter.", g_extra_turn());
  309.     }
  310.     tprintf(buf, ".\n");
  311.     }
  312.     if (g_rt_for_game() > 0) {
  313.     tprintf(buf, "Entire game can last up to %d minutes.\n",
  314.         g_rt_for_game() / 60);
  315.     }
  316.     if (g_rt_per_turn() > 0) {
  317.     tprintf(buf, "Each turn can last up to %d minutes.\n",
  318.         g_rt_per_turn() / 60);
  319.     }
  320.     if (g_rt_per_side() > 0) {
  321.     tprintf(buf, "Each side gets a total %d minutes to act.\n",
  322.         g_rt_per_side() / 60);
  323.     }
  324.     if (g_units_in_game_max() >= 0) {
  325.     tprintf(buf, "Limited to no more than %d units in all.\n", g_units_in_game_max());
  326.     }
  327.     if (g_units_per_side_max() >= 0) {
  328.     tprintf(buf, "Limited to no more than %d units per side.\n", g_units_per_side_max());
  329.     }
  330.     /* (should list out random event types?) */
  331.     tprintf(buf, "\n");
  332.     tprintf(buf, "Lowest possible temperature is %d, at an elevation of %d.\n",
  333.         g_temp_floor(), g_temp_floor_elev());
  334.     tprintf(buf, "\nUnit Types:\n");
  335.     for_all_unit_types(u) {
  336.     tprintf(buf, "  %s", u_type_name(u));
  337.     if (!empty_string(u_help(u)))
  338.       tprintf(buf, " (%s)", u_help(u));
  339.     tprintf(buf, "\n");
  340. #ifdef DESIGNERS
  341.     /* Show designers a bit more. */
  342.     if (numdesigners > 0) {
  343.         tprintf(buf, "    [");
  344.         if (!empty_string(u_uchar(u)))
  345.           tprintf(buf, "char '%s'", u_uchar(u));
  346.         else
  347.           tprintf(buf, "no char");
  348.         if (!empty_string(u_image_name(u)))
  349.           tprintf(buf, ", image \"%s\"", u_image_name(u));
  350.         if (!empty_string(u_color(u)))
  351.           tprintf(buf, ", color \"%s\"", u_color(u));
  352.         if (!empty_string(u_generic_name(u)))
  353.           tprintf(buf, ", generic name \"%s\"", u_generic_name(u));
  354.         if (u_desc_format(u) != lispnil) {
  355.             tprintf(buf, ", special format");
  356.         }
  357.         tprintf(buf, "]\n");
  358.     }
  359. #endif /* DESIGNERS */
  360.     }
  361.     tprintf(buf, "\nTerrain Types:\n");
  362.     for_all_terrain_types(t) {
  363.     tprintf(buf, "  %s", t_type_name(t));
  364.     if (!empty_string(t_help(t)))
  365.       tprintf(buf, " (%s)", t_help(t));
  366.     tprintf(buf, "\n");
  367. #ifdef DESIGNERS
  368.     /* Show designers a bit more. */
  369.     if (numdesigners > 0) {
  370.         tprintf(buf, "    [");
  371.         if (!empty_string(t_char(t)))
  372.           tprintf(buf, "char '%s'", t_char(t));
  373.         else
  374.           tprintf(buf, "no char");
  375.         if (!empty_string(t_image_name(t)))
  376.           tprintf(buf, ", image \"%s\"", t_image_name(t));
  377.         if (!empty_string(t_color(t)))
  378.           tprintf(buf, ", color \"%s\"", t_color(t));
  379.         if (t_desc_format(t) != lispnil) {
  380.             tprintf(buf, ", special format");
  381.         }
  382.         tprintf(buf, "]\n");
  383.     }
  384. #endif /* DESIGNERS */
  385.     }
  386.     if (nummtypes > 0) {
  387.     tprintf(buf, "\nMaterial Types:\n");
  388.     for_all_material_types(m) {
  389.         tprintf(buf, "  %s", m_type_name(m));
  390.         if (!empty_string(m_help(m)))
  391.           tprintf(buf, " (%s)", m_help(m));
  392.         tprintf(buf, "\n");
  393. #ifdef DESIGNERS
  394.         /* Show designers a bit more. */
  395.         if (numdesigners > 0) {
  396.         tprintf(buf, "    [");
  397.         if (!empty_string(m_char(m)))
  398.           tprintf(buf, "char '%s'", m_char(m));
  399.         else
  400.           tprintf(buf, "no char");
  401.         if (!empty_string(m_image_name(m)))
  402.           tprintf(buf, ", image \"%s\"", m_image_name(m));
  403.         if (!empty_string(m_color(m)))
  404.           tprintf(buf, ", color \"%s\"", m_color(m));
  405.         if (m_desc_format(m) != lispnil) {
  406.             tprintf(buf, ", special format");
  407.         }
  408.         tprintf(buf, "]\n");
  409.         }
  410. #endif /* DESIGNERS */
  411.     }
  412.     }
  413. }
  414.  
  415. int any_mp_to_enter_unit PARAMS ((int u));
  416. int any_mp_to_leave_unit PARAMS ((int u));
  417.  
  418. int
  419. any_mp_to_enter_unit(u)
  420. int u;
  421. {
  422.     int u2;
  423.     
  424.     for_all_unit_types(u2) {
  425.     if (uu_mp_to_enter(u, u2) != 0) return TRUE;
  426.     }
  427.     return FALSE;
  428. }
  429.  
  430. int
  431. any_mp_to_leave_unit(u)
  432. int u;
  433. {
  434.     int u2;
  435.     
  436.     for_all_unit_types(u2) {
  437.     if (uu_mp_to_leave(u, u2) != 0) return TRUE;
  438.     }
  439.     return FALSE;
  440. }
  441.  
  442. void fraction_desc PARAMS ((char *buf, int n));
  443.  
  444. void
  445. fraction_desc(buf, n)
  446. char *buf;
  447. int n;
  448. {
  449.     sprintf(buf, "%d.%d", n / 100, n % 100);
  450. }
  451.  
  452. /* Full details on the given type of unit. */
  453.  
  454. /* (The defaults should come from the *.def defaults!!) */
  455.  
  456. void
  457. describe_utype(u, key, buf)
  458. int u;
  459. char *key, *buf;
  460. {
  461.     char sidetmpbuf[BUFSIZE];
  462.     int m, first;
  463.     Side *side;
  464.  
  465.     append_help_phrase(buf, u_help(u));
  466.     if (u_point_value(u) > 0) {
  467.     tprintf(buf, "     (point value %d)\n", u_point_value(u));
  468.     }
  469.     if (u_can_be_self(u)) {
  470.         tprintf(buf, "Can be self-unit");
  471.         if (u_self_changeable(u))
  472.       tprintf(buf, "; side may choose another to be self-unit");
  473.         if (u_self_resurrects(u))
  474.       tprintf(buf, "; if dies, another becomes self-unit");
  475.         tprintf(buf, ".\n");
  476.     }
  477.     if (u_possible_sides(u) != lispnil) {
  478.     tprintf(buf, "Possible sides (in this game): ");
  479.     first = TRUE;
  480.     for_all_sides_plus_indep(side) {
  481.         if (type_allowed_on_side(u, side)) {
  482.         if (first)
  483.           first = FALSE;
  484.         else
  485.           tprintf(buf, ", ");
  486.         tprintf(buf, "%s", shortest_side_title(side, sidetmpbuf));
  487.         }
  488.         }
  489.         tprintf(buf, ".\n");
  490.     }
  491.     if (u_type_in_game_max(u) >= 0) {
  492.     tprintf(buf, "At most %d allowed in a game.\n", u_type_in_game_max(u));
  493.     }
  494.     if (u_type_per_side_max(u) >= 0) {
  495.     tprintf(buf, "At most %d allowed on each side in a game.\n", u_type_per_side_max(u));
  496.     }
  497.     if (u_acp(u) > 0) {
  498.         tprintf(buf, "Gets %d action points each turn", u_acp(u));
  499.         if (u_acp_min(u) != 0) {
  500.         tprintf(buf, ", can go down to %d acp", u_acp_min(u));
  501.         }
  502.         if (u_acp_max(u) != -1) {
  503.         tprintf(buf, ", can go up to %d acp", u_acp_max(u));
  504.         }
  505.         if (u_free_acp(u) != 0) {
  506.         tprintf(buf, ", %d free", u_free_acp(u));
  507.         }
  508.         tprintf(buf, ".\n");
  509.     } else {
  510.     tprintf(buf, "Does not act.\n");
  511.     }
  512.     if (!u_direct_control(u)) {
  513.     tprintf(buf, "Cannot be controlled directly by side.\n");
  514.     }
  515.     if (u_speed(u) > 0) {
  516.     tprintf(buf, "Speed (mp/acp ratio) is %d.%d cells/acp.\n",
  517.         u_speed(u) / 100, u_speed(u) % 100);
  518.     if (u_speed_min(u) != 0 || u_speed_max(u) != 9999) {
  519.         tprintf(buf, "Speed variation limited to between %d.%d and %d.%d.\n",
  520.             u_speed_min(u) / 100, u_speed_min(u) % 100,
  521.             u_speed_max(u) / 100, u_speed_max(u) % 100);
  522.     }
  523.     if (u_speed_wind_effect(u) != lispnil) {
  524.         tprintf(buf, "Wind affects speed.\n");
  525.     }
  526.     if (u_speed_damage_effect(u) != lispnil) {
  527.         tprintf(buf, "Damage affects speed.\n");
  528.     }
  529.     ut_table_row_desc(spbuf, u, ut_mp_to_enter, NULL);
  530.     tprintf(buf, "MP to enter cell: %s.\n", spbuf);
  531.     ut_table_row_desc(spbuf, u, ut_mp_to_leave, NULL);
  532.     tprintf(buf, "MP to leave cell: %s.\n", spbuf);
  533.     if (any_mp_to_enter_unit(u)) {
  534.         uu_table_row_desc(spbuf, u, uu_mp_to_enter, NULL);
  535.         tprintf(buf, "MP to enter unit: %s.\n", spbuf);
  536.     }
  537.     if (any_mp_to_leave_unit(u)) {
  538.         uu_table_row_desc(spbuf, u, uu_mp_to_leave, NULL);
  539.         tprintf(buf, "MP to leave unit: %s.\n", spbuf);
  540.     }
  541.     if (u_mp_to_leave_world(u) >= 0) {
  542.         tprintf(buf, "%d MP to leave the world entirely.\n", u_mp_to_leave_world(u));
  543.     }
  544.     if (u_free_mp(u) > 0) {
  545.         tprintf(buf, "Gets up to %d free MP if needed to move.\n", u_free_mp(u));
  546.     }
  547.     if (u_acp_to_move(u) > 0) {
  548.         tprintf(buf, "Uses %d ACP to move.\n", u_acp_to_move(u));
  549.     } else {
  550.         tprintf(buf, "Cannot move by self.\n");
  551.     }
  552.     } else {
  553.     tprintf(buf, "Does not move.\n");
  554.     }
  555.     tprintf(buf, "Hit Points: %d.", u_hp_max(u));
  556.     if (u_parts(u) > 1) {
  557.     tprintf(buf, "  Parts: %d.", u_parts(u));
  558.     }
  559.     if (u_hp_recovery(u) != 0) {
  560.     tprintf(buf, "  Recovers by %d.%d HP each turn.",
  561.         u_hp_recovery(u) / 100, u_hp_recovery(u) % 100);
  562.     }
  563.     tprintf(buf, "\n");
  564.     if (u_capacity(u) != 0) {
  565.     tprintf(buf, "Generic capacity for units is %d.\n",
  566.         u_capacity(u));
  567.     }
  568.     if (u_cxp_max(u) != 0) {
  569.     tprintf(buf, "Combat experience max: %d.\n", u_cxp_max(u));
  570.     }
  571.     if (u_cp(u) != 1) {
  572.     tprintf(buf, "Construction points to complete: %d.\n", u_cp(u));
  573.     }
  574.     if (u_tech_to_see(u) != 0) {
  575.     tprintf(buf, "Tech to see: %d.\n", u_tech_to_see(u));
  576.     }
  577.     if (u_tech_to_own(u) != 0) {
  578.     tprintf(buf, "Tech to own: %d.\n", u_tech_to_own(u));
  579.     }
  580.     if (u_tech_to_use(u) != 0) {
  581.     tprintf(buf, "Tech to use: %d.\n", u_tech_to_use(u));
  582.     }
  583.     if (u_tech_to_build(u) != 0) {
  584.     tprintf(buf, "Tech to build: %d.\n", u_tech_to_build(u));
  585.     }
  586.     if (u_tech_max(u) != 0) {
  587.     tprintf(buf, "Tech max: %d.\n", u_tech_max(u));
  588.     }
  589.     if (u_tech_max(u) != 0 && u_tech_per_turn_max(u) != 9999) {
  590.     tprintf(buf, "Tech increase per turn max: %d.\n", u_tech_per_turn_max(u));
  591.     }
  592.     if (u_tech_from_ownership(u) != 0) {
  593.     tprintf(buf, "Tech guaranteed by ownership: %d.\n", u_tech_from_ownership(u));
  594.     }
  595.     if (u_tech_leakage(u) != 0) {
  596.     tprintf(buf, "Tech leakage: %d.\n", u_tech_leakage(u));
  597.     }
  598.     if (u_acp(u) > 0
  599.         && type_can_research(u) > 0
  600.         ) {
  601.         tprintf(buf, "\nResearch:\n");
  602.         uu_table_row_desc(spbuf, u, uu_acp_to_research, NULL);
  603.     tprintf(buf, "ACP to research: %s.\n", spbuf);
  604.         uu_table_row_desc(spbuf, u, uu_tech_per_research, fraction_desc);
  605.     tprintf(buf, "  Tech gained: %s.\n", spbuf);
  606.     }
  607.     if (u_acp(u) > 0
  608.         && (type_can_create(u) > 0
  609.             || type_can_complete(u) > 0
  610.         )) {
  611.         tprintf(buf, "\nConstruction:\n");
  612.         if (type_can_create(u) > 0) {
  613.         uu_table_row_desc(spbuf, u, uu_acp_to_create, NULL);
  614.         tprintf(buf, "ACP to create: %s.\n", spbuf);
  615.          uu_table_row_desc(spbuf, u, uu_create_range, NULL);
  616.          tprintf(buf, "  Creation distance max: %s.\n", spbuf);
  617.          uu_table_row_desc(spbuf, u, uu_creation_cp, NULL);
  618.          tprintf(buf, "  Completeness upon creation: %s.\n", spbuf);
  619.     }
  620.         if (type_can_complete(u) > 0) {
  621.         uu_table_row_desc(spbuf, u, uu_acp_to_build, NULL);
  622.         tprintf(buf, "ACP to build: %s.\n", spbuf);
  623.          uu_table_row_desc(spbuf, u, uu_cp_per_build, NULL);
  624.          tprintf(buf, "  Completeness added per build: %s.\n", spbuf);
  625.         }
  626.         if (u_cp_per_self_build(u) > 0) {
  627.         tprintf(buf, "Can finish building self at %d cp, will add %d cp per action.\n",
  628.             u_cp_to_self_build(u), u_cp_per_self_build(u));
  629.         }
  630.         /* Toolup help. */
  631.         if (type_can_toolup(u)) {
  632.         uu_table_row_desc(spbuf, u, uu_acp_to_toolup, NULL);
  633.         tprintf(buf, "ACP to toolup: %s.\n", spbuf);
  634.         uu_table_row_desc(spbuf, u, uu_tp_per_toolup, NULL);
  635.         tprintf(buf, "  TP/toolup action: %s.\n", spbuf);
  636.         /* (should put these with type beING built...) */
  637.         uu_table_row_desc(spbuf, u, uu_tp_to_build, NULL);
  638.         tprintf(buf, "  TP to build: %s.\n", spbuf);
  639.         uu_table_row_desc(spbuf, u, uu_tp_max, NULL);
  640.         tprintf(buf, "  TP max: %s.\n", spbuf);
  641.         }
  642.         
  643.     }
  644.     if (u_acp(u) > 0
  645.         && (u_acp_to_fire(u) > 0
  646.         || type_can_attack(u) > 0
  647.         || u_acp_to_detonate(u) > 0
  648.         )) {
  649.     tprintf(buf, "\nCombat:\n");
  650.     if (type_can_attack(u) > 0) {
  651.         uu_table_row_desc(spbuf, u, uu_acp_to_attack, NULL);
  652.         tprintf(buf, "Can attack (ACP %s).\n", spbuf);
  653.         if (uu_table_row_not_default(u, uu_attack_range, 1)) {
  654.         uu_table_row_desc(spbuf, u, uu_attack_range, NULL);
  655.         tprintf(buf, "Attack range is %s.\n", spbuf);
  656.         uu_table_row_desc(spbuf, u, uu_attack_range_min, NULL);
  657.         tprintf(buf, "Attack range min is %s.\n", spbuf);
  658.         }
  659.     }
  660.        if (u_acp_to_fire(u) > 0) {
  661.         tprintf(buf, "Can fire (%d ACP), at ranges", u_acp_to_fire(u));
  662.         if (u_range_min(u) > 0) {
  663.         tprintf(buf, " from %d", u_range_min(u));
  664.         }
  665.         tprintf(buf, " up to %d", u_range(u));
  666.         tprintf(buf, ".\n");
  667.     }
  668.     if (type_can_capture(u) > 0) {
  669.         uu_table_row_desc(spbuf, u, uu_acp_to_capture, NULL);
  670.         tprintf(buf, "Can capture (ACP %s).\n", spbuf);
  671.         uu_table_row_desc(spbuf, u, uu_capture, NULL);
  672.         tprintf(buf, "Chance to capture: %s.\n", spbuf);
  673.         if (uu_table_row_not_default(u, uu_indep_capture, -1)) {
  674.         uu_table_row_desc(spbuf, u, uu_indep_capture, NULL);
  675.         tprintf(buf, "Chance to capture indep: %s.\n", spbuf);
  676.         }
  677.     }
  678.     if (u_acp_to_detonate(u) > 0) {
  679.         tprintf(buf, "Can detonate self (%d ACP)", u_acp_to_detonate(u));
  680.         if (u_hp_per_detonation(u) < u_hp_max(u)) {
  681.         tprintf(buf, ", losing %d HP per detonation",
  682.             u_hp_per_detonation(u));
  683.         }
  684.         tprintf(buf, ".\n");
  685.         if (u_detonate_on_death(u)) {
  686.         tprintf(buf, "%d%% chance to detonate if mortally hit in combat.\n",
  687.             u_detonate_on_death(u));
  688.         }
  689.         uu_table_row_desc(spbuf, u, uu_detonate_on_hit, NULL);
  690.         tprintf(buf, "Chance to detonate upon being hit: %s", spbuf);
  691.         tprintf(buf, ".\n");
  692.         uu_table_row_desc(spbuf, u, uu_detonate_on_capture, NULL);
  693.         tprintf(buf, "Chance to detonate upon capture: %s", spbuf);
  694.         tprintf(buf, ".\n");
  695.         uu_table_row_desc(spbuf, u, uu_detonation_range, NULL);
  696.         tprintf(buf, "Range of detonation effect is %s", spbuf);
  697.         tprintf(buf, ".\n");
  698.     }
  699.     uu_table_row_desc(spbuf, u, uu_hit, NULL);
  700.     tprintf(buf, "Hit chances are %s", spbuf);
  701.     tprintf(buf, ".\n");
  702.     uu_table_row_desc(spbuf, u, uu_damage, dice_desc);
  703.     tprintf(buf, "Damages are %s", spbuf);
  704.     tprintf(buf, ".\n");
  705.        if (u_acp_to_fire(u) > 0) {
  706.         /* (should describe fire damage if different) */
  707.        }
  708.     }
  709.     if (uu_table_row_not_default(u, uu_protection, 100)) {
  710.     uu_table_row_desc(spbuf, u, uu_protection, NULL);
  711.     tprintf(buf, "Protection of occupants/transport is %s.\n", spbuf);
  712.     }
  713.     if (uu_table_row_not_default(u, uu_retreat_chance, 0)) {
  714.     uu_table_row_desc(spbuf, u, uu_retreat_chance, NULL);
  715.     tprintf(buf, "Chance to retreat from combat is %s.\n", spbuf);
  716.     }
  717.     if (u_wrecked_type(u) != NONUTYPE) {
  718.         tprintf(buf, "Becomes %s when destroyed.\n",
  719.         u_type_name(u_wrecked_type(u)));
  720.     }
  721.     if (u_acp(u) > 0
  722.         && (u_acp_to_change_side(u) > 0
  723.             || u_acp_to_disband(u) > 0
  724.             || u_acp_to_transfer_part(u) > 0
  725.             )) {
  726.     tprintf(buf, "\nOther Actions:\n");
  727.     if (u_acp_to_change_side(u) > 0) {
  728.         tprintf(buf, "Can be given to another side (%d ACP).\n",
  729.             u_acp_to_change_side(u));
  730.     }
  731.     if (u_acp_to_disband(u) > 0) {
  732.         tprintf(buf, "Can be disbanded (%d ACP)", u_acp_to_disband(u));
  733.         if (u_hp_per_disband(u) < u_hp_max(u)) {
  734.             tprintf(buf, ", losing %d HP per action", u_hp_per_disband(u));
  735.         }
  736.         tprintf(buf, ".\n"); 
  737.     }
  738.     if (u_acp_to_transfer_part(u) > 0) {
  739.         tprintf(buf, "Can transfer parts (%d ACP).\n",
  740.             u_acp_to_transfer_part(u));
  741.     }
  742.     if (uu_table_row_not_default(u, uu_acp_to_repair, 0)) {
  743.         uu_table_row_desc(spbuf, u, uu_acp_to_repair, NULL);
  744.         tprintf(buf, "ACP to repair is %s.\n", spbuf);
  745.         uu_table_row_desc(spbuf, u, uu_repair, NULL);
  746.         tprintf(buf, "Repair performance is %s.\n", spbuf);
  747.         uu_table_row_desc(spbuf, u, uu_hp_to_repair, NULL);
  748.         tprintf(buf, "Min HP to repair is %s.\n", spbuf);
  749.     }
  750.     }
  751.     if (!g_see_all()) {
  752.         tprintf(buf, "\nVision:\n");
  753.     tprintf(buf, "%d%% chance to be seen at outset of game.\n",
  754.         u_already_seen(u));
  755.     tprintf(buf, "%d%% chance to be seen at outset of game if independent.\n",
  756.         u_already_seen_indep(u));
  757.     if (u_see_always(u))
  758.       tprintf(buf, "Always seen if terrain has been seen.\n");
  759.     /* (should put other _see_ things here) */
  760.     switch (u_vision_range(u)) {
  761.       case -1:
  762.         tprintf(buf, "Can never see other units.\n");
  763.         break;
  764.       case 0:
  765.         tprintf(buf, "Can see other units at own location.\n");
  766.         break;
  767.       case 1:
  768.         /* Default range, no need to say anything. */
  769.         break;
  770.       default:
  771.         tprintf(buf, "Can see units up to %d cells away.\n", u_vision_range(u));
  772.         break;
  773.     }
  774.     if (u_vision_range(u) >= 0) {
  775.       uu_table_row_desc(spbuf, u, uu_see_at, NULL);
  776.       tprintf(buf, "Chance to see if in same cell is %s", spbuf);
  777.       tprintf(buf, ".\n");
  778.     }
  779.     if (u_vision_range(u) >= 1) {
  780.       uu_table_row_desc(spbuf, u, uu_see_adj, NULL);
  781.       tprintf(buf, "Chance to see if adjacent is %s", spbuf);
  782.       tprintf(buf, ".\n");
  783.     }
  784.     if (u_vision_range(u) >= 2) {
  785.       uu_table_row_desc(spbuf, u, uu_see, NULL);
  786.       tprintf(buf, "Chance to see in general is %s", spbuf);
  787.       tprintf(buf, ".\n");
  788.     }
  789.     }
  790.     if (nummtypes > 0) {
  791.     tprintf(buf, "\nMaterial Handling:\n");
  792.     for_all_material_types(m) {
  793.         tprintf(buf, "  %s", m_type_name(m));
  794.         if (um_base_production(u, m) > 0) {
  795.         tprintf(buf, ", %d base production", um_base_production(u, m));
  796.         }
  797.         if (um_storage_x(u, m) > 0) {
  798.         tprintf(buf, ", %d storage", um_storage_x(u, m));
  799.         if (um_initial(u, m) > 0) {
  800.             tprintf(buf, " (%d initially)", min(um_initial(u, m), um_storage_x(u, m)));
  801.         }
  802.         }
  803.         if (um_base_consumption(u, m) > 0) {
  804.         tprintf(buf, ", %d base consumption", um_base_consumption(u, m));
  805.         }
  806.         if (um_inlength(u, m) > 0) {
  807.         tprintf(buf, ", receive from %d cells away", um_inlength(u, m));
  808.         }
  809.         if (um_outlength(u, m) > 0) {
  810.         tprintf(buf, ", send up to %d cells away", um_outlength(u, m));
  811.         }
  812.         tprintf(buf, "\n");
  813.     }
  814.     }
  815.     /* (should display weather interaction here) */
  816.     if (u_spy_chance(u) > 0 /* and random event in use */) {
  817.     tprintf(buf, "%d%% chance to spy, on units up to %d away.",
  818.         u_spy_chance(u), u_spy_range(u));
  819.     }
  820.     if (u_revolt(u) > 0 /* and random event in use */) {
  821.     tprintf(buf, "%d.%d%% chance of revolt.\n",
  822.         u_revolt(u) / 100, u_revolt(u) % 100);
  823.     }
  824.     /* Display the designer's notes for this type. */
  825.     if (u_notes(u) != lispnil) {
  826.     tprintf(buf, "\nNotes:\n");
  827.     append_notes(buf, u_notes(u));
  828.     }
  829. }
  830.  
  831. void
  832. describe_mtype(m, key, buf)
  833. int m;
  834. char *key, *buf;
  835. {
  836.     append_help_phrase(buf, m_help(m));
  837.     if (m_people(m) > 0) {
  838.     tprintf(buf, "1 of this represents %d individuals.", m_people(m));
  839.     }
  840.     /* Display the designer's notes for this type. */
  841.     if (m_notes(m) != lispnil) {
  842.     tprintf(buf, "\nNotes:\n");
  843.     append_notes(buf, m_notes(m));
  844.     }
  845. }
  846.  
  847. void
  848. describe_ttype(t, key, buf)
  849. int t;
  850. char *key, *buf;
  851. {
  852.     int m, ct;
  853.  
  854.     append_help_phrase(buf, t_help(t));
  855.     switch (t_subtype(t)) {
  856.       case cellsubtype:
  857.     break;
  858.       case bordersubtype:
  859.     tprintf(buf, " (a border type)\n");
  860.     break;
  861.       case connectionsubtype:
  862.     tprintf(buf, " (a connection type)\n");
  863.     break;
  864.       case coatingsubtype:
  865.     tprintf(buf, " (a coating type)\n");
  866.     break;
  867.     }
  868.     tprintf(buf, "Generic unit capacity is %d.\n", t_capacity(t));
  869.     if (minelev != maxelev /* should be "elevscanvary" */) {
  870.     if (t_elev_min(t) == t_elev_max(t)) {
  871.         tprintf(buf, "Elevation is always %d.\n",
  872.             t_elev_min(t));
  873.     } else {
  874.         tprintf(buf, "Elevations fall between %d and %d.\n",
  875.             t_elev_min(t), t_elev_max(t));
  876.     }
  877.     }
  878.     if (t_thickness(t) > 0) {
  879.     tprintf(buf, "Thickness is %d.\n", t_thickness(t));
  880.     }
  881.     if (any_temp_variation) {
  882.     if (t_temp_min(t) == t_temp_max(t)) {
  883.         tprintf(buf, "Temperature is always %d.\n",
  884.             t_temp_min(t));
  885.     } else {
  886.         tprintf(buf, "Temperatures fall between %d and %d, averaging %d.\n",
  887.             t_temp_min(t), t_temp_max(t), t_temp_avg(t));
  888.     }
  889.     }
  890.     if (any_wind_variation) {
  891.     if (t_wind_force_min(t) == t_wind_force_max(t)) {
  892.         tprintf(buf, "Wind force is always %d.\n",
  893.             t_wind_force_min(t));
  894.     } else {
  895.         tprintf(buf, "Wind forces fall between %d and %d, averaging %d.\n",
  896.             t_wind_force_min(t), t_wind_force_max(t), t_wind_force_avg(t));
  897.     }
  898.     if (t_wind_force_variability(t) > 0) {
  899.         tprintf(buf, "%d%% chance each turn that wind force will change.\n",
  900.             t_wind_force_variability(t));
  901.     }
  902.     if (t_wind_variability(t) > 0) {
  903.         tprintf(buf, "%d%% chance each turn that wind direction will change.\n",
  904.             t_wind_variability(t));
  905.     }
  906.     }
  907.     if (any_clouds) {
  908.     if (t_clouds_min(t) == t_clouds_max(t)) {
  909.         tprintf(buf, "Cloud cover is always %d.\n",
  910.             t_clouds_min(t));
  911.     } else {
  912.         tprintf(buf, "Cloud cover falls between %d and %d\n",
  913.             t_clouds_min(t), t_clouds_max(t));
  914.     }
  915.     }
  916.     /* Display relationships with materials. */
  917.     if (nummtypes > 0) {
  918.     for_all_material_types(m) {
  919.         if (tm_storage_x(t, m) > 0) {
  920.             tprintf(buf, "Can store up to %d %s", tm_storage_x(t, m), m_type_name(m));
  921.             tprintf(buf, " (normally starts game with %d)",
  922.                 min(tm_initial(t, m), tm_storage_x(t, m)));
  923.             tprintf(buf, ".\n");
  924.         }
  925.         if (tm_production(t, m) > 0 || tm_consumption(t, m) > 0) {
  926.         tprintf(buf, " Produces %d and consumes %d each turn.\n",
  927.             tm_production(t, m), tm_consumption(t, m));
  928.         }
  929.     }
  930.     }
  931.     /* Display relationships with any coating terrain types. */
  932.     if (numcoattypes > 0) {
  933.     tprintf(buf, "Coatings:\n");
  934.         for_all_terrain_types(ct) {
  935.         if (t_is_coating(ct)) {
  936.         tprintf(buf, "%s coats, depths %d up to %d",
  937.             t_type_name(ct), tt_coat_min(ct, t), tt_coat_max(ct, t));
  938.         }
  939.         }
  940.     }
  941.     /* (should display damaged and exhaustion types) */
  942.     /* Display the designer's notes for this type. */
  943.     if (t_notes(t) != lispnil) {
  944.     tprintf(buf, "\nNotes:\n");
  945.     append_notes(buf, t_notes(t));
  946.     }
  947. }
  948.  
  949. void
  950. describe_scorekeepers(arg, key, buf)
  951. int arg;
  952. char *key, *buf;
  953. {
  954.     int i = 1;
  955.     Scorekeeper *sk;
  956.  
  957.     if (scorekeepers == NULL) {
  958.     tprintf(buf, "No scores are being kept.");
  959.     } else {
  960.     for_all_scorekeepers(sk) {
  961.         if (numscorekeepers > 1) {
  962.         tprintf(buf, "%d.  ", i++);
  963.         }
  964.         if (symbolp(sk->body)
  965.         && match_keyword(sk->body, K_LAST_SIDE_WINS)) {
  966.         tprintf(buf, "The last side left in the game wins.");
  967.         /* (should mention point values also) */
  968.         } else {
  969.         tprintf(buf, "(an indescribably complicated scorekeeper)");
  970.         }
  971.         tprintf(buf, "\n");
  972.     }
  973.     }
  974. }
  975.  
  976. /* List each synthesis method and its parameters. */
  977.  
  978. void
  979. describe_setup(arg, key, buf)
  980. int arg;
  981. char *key, *buf;
  982. {
  983.     int u, methkey;
  984.     Obj *synthlist, *methods, *method;
  985.     
  986.     tprintf(buf, "Synthesis done when setting up this game:\n");
  987.     synthlist = g_synth_methods();
  988.     for (methods = synthlist; methods != lispnil; methods = cdr(methods)) {
  989.     method = car(methods);
  990.     if (symbolp(method)) {
  991.         methkey = keyword_code(c_string(method));
  992.         switch (methkey) {
  993.           case K_MAKE_COUNTRIES:
  994.         tprintf(buf, "\nCountries:");
  995.         describe_synth_run(buf, methkey);
  996.         tprintf(buf, "\n");
  997.         tprintf(buf, "  %d cells across, between %d and %d cells apart.\n",
  998.             2 * g_radius_min() + 1,
  999.             g_separation_min(), g_separation_max());
  1000.         if (t_property_not_default(t_country_min, 0)) {
  1001.             t_property_desc(spbuf, t_country_min, NULL);
  1002.             tprintf(buf, "  Minimum terrain in each country: %s.\n", spbuf);
  1003.         }
  1004.         if (t_property_not_default(t_country_max, -1)) {
  1005.             t_property_desc(spbuf, t_country_max, NULL);
  1006.             tprintf(buf, "  Maximum terrain in each country: %s.\n", spbuf);
  1007.         }
  1008.         if (u_property_not_default(u_start_with, 0)) {
  1009.             u_property_desc(spbuf, u_start_with, NULL);
  1010.             tprintf(buf, "  Start with: %s.\n", spbuf);
  1011.         }
  1012.         if (u_property_not_default(u_indep_near_start, 0)) {
  1013.             u_property_desc(spbuf, u_indep_near_start, NULL);
  1014.             tprintf(buf, "  Independents nearby: %s.\n", spbuf);
  1015.         }
  1016.         tprintf(buf, "  Favored terrain:\n");
  1017.         for_all_unit_types(u) {
  1018.             if (u_start_with(u) > 0 || u_indep_near_start(u)) {
  1019.             tprintf(buf, "  %s:", u_type_name(u));
  1020.             ut_table_row_desc(spbuf, u, ut_favored, NULL);
  1021.             tprintf(buf, " %s\n", spbuf);
  1022.             }
  1023.         }
  1024.         if (g_radius_max() != 0) {
  1025.             tprintf(buf, "Country growth:\n");
  1026.             if (g_radius_max() == -1) {
  1027.             tprintf(buf, "  Up to entire world");
  1028.             } else {
  1029.             tprintf(buf, "  Up to %d cells across", 2 * g_radius_max() + 1);
  1030.             }
  1031.             tprintf(buf, ", %d chance to stop if blocked.\n", g_growth_stop());
  1032.             if (t_property_not_default(t_country_growth, 100)) {
  1033.             t_property_desc(spbuf, t_country_max, NULL);
  1034.             tprintf(buf, "  Growth chance, by terrain: %s.\n", spbuf);
  1035.             }
  1036.             if (t_property_not_default(t_country_takeover, 0)) {
  1037.             t_property_desc(spbuf, t_country_takeover, NULL);
  1038.             tprintf(buf, "  Takeover chance, by terrain: %s.\n", spbuf);
  1039.             }
  1040.             if (u_property_not_default(u_unit_growth, 0)) {
  1041.             u_property_desc(spbuf, u_unit_growth, NULL);
  1042.             tprintf(buf, "  Chance for additional unit: %s.\n", spbuf);
  1043.             }
  1044.             if (u_property_not_default(u_indep_growth, 0)) {
  1045.             u_property_desc(spbuf, u_indep_growth, NULL);
  1046.             tprintf(buf, "  Chance for additional independent unit: %s.\n", spbuf);
  1047.             }
  1048.             if (u_property_not_default(u_unit_takeover, 0)) {
  1049.             u_property_desc(spbuf, u_unit_takeover, NULL);
  1050.             tprintf(buf, "  Chance to take over units: %s.\n", spbuf);
  1051.             }
  1052.             if (u_property_not_default(u_indep_takeover, 0)) {
  1053.             u_property_desc(spbuf, u_indep_takeover, NULL);
  1054.             tprintf(buf, "  Chance to take over independent unit: %s.\n", spbuf);
  1055.             }
  1056.             if (u_property_not_default(u_country_units_max, -1)) {
  1057.             u_property_desc(spbuf, u_country_units_max, NULL);
  1058.             tprintf(buf, "  Maximum units in country: %s.\n", spbuf);
  1059.             }
  1060.             if (t_property_not_default(t_country_people, 0)) {
  1061.             t_property_desc(spbuf, t_country_people, NULL);
  1062.             tprintf(buf, "  People takeover chance, by terrain: %s.\n", spbuf);
  1063.             }
  1064.         }
  1065.         break;
  1066.           case K_MAKE_EARTHLIKE_TERRAIN:
  1067.         tprintf(buf, "\nEarthlike terrain:");
  1068.         describe_synth_run(buf, methkey);
  1069.         tprintf(buf, "\n");
  1070.         break;
  1071.           case K_MAKE_FRACTAL_PTILE_TERRAIN:
  1072.         tprintf(buf, "\nFractal percentile terrain:");
  1073.         describe_synth_run(buf, methkey);
  1074.         tprintf(buf, "\n");
  1075.         tprintf(buf, "  Alt blobs density %d, size %d, height %d\n",
  1076.             g_alt_blob_density(), g_alt_blob_size(), g_alt_blob_height());
  1077.         tprintf(buf, "    %d smoothing passes\n", g_alt_smoothing());
  1078.         t_property_desc(spbuf, t_alt_min, NULL);
  1079.         tprintf(buf, "    Lower percentiles: %s.\n", spbuf);
  1080.         t_property_desc(spbuf, t_alt_max, NULL);
  1081.         tprintf(buf, "    Upper percentiles: %s.\n", spbuf);
  1082.         tprintf(buf, "  Wet blobs density %d, size %d, height %d\n",
  1083.             g_wet_blob_density(), g_wet_blob_size(), g_wet_blob_height());
  1084.         tprintf(buf, "    %d smoothing passes\n", g_wet_smoothing());
  1085.         t_property_desc(spbuf, t_wet_min, NULL);
  1086.         tprintf(buf, "    Lower percentiles: %s.\n", spbuf);
  1087.         t_property_desc(spbuf, t_wet_max, NULL);
  1088.         tprintf(buf, "    Upper percentiles: %s.\n", spbuf);
  1089.         break;
  1090.           case K_MAKE_INDEPENDENT_UNITS:
  1091.         tprintf(buf, "\nIndependent units:");
  1092.         describe_synth_run(buf, methkey);
  1093.         tprintf(buf, "\n");
  1094.         /* (should show indep density and people) */
  1095.         break;
  1096.           case K_MAKE_INITIAL_MATERIALS:
  1097.         tprintf(buf, "\nMaterials:");
  1098.         describe_synth_run(buf, methkey);
  1099.         tprintf(buf, "\n");
  1100.         /* (should show unit and terrain initial supply) */
  1101.         break;
  1102.           case K_MAKE_MAZE_TERRAIN:
  1103.         tprintf(buf, "\nMaze terrain:");
  1104.         describe_synth_run(buf, methkey);
  1105.         tprintf(buf, "\n");
  1106.         break;
  1107.           case K_MAKE_RANDOM_DATE:
  1108.         tprintf(buf, "\nRandom date:");
  1109.         describe_synth_run(buf, methkey);
  1110.         tprintf(buf, "\n");
  1111.         break;
  1112.           case K_MAKE_RANDOM_TERRAIN:
  1113.         tprintf(buf, "\nRandom terrain:");
  1114.         describe_synth_run(buf, methkey);
  1115.         tprintf(buf, "\n");
  1116.         break;
  1117.           case K_MAKE_RIVERS:
  1118.         tprintf(buf, "\nRivers:");
  1119.         describe_synth_run(buf, methkey);
  1120.         tprintf(buf, "\n");
  1121.         if (t_property_not_default(t_river_chance, 0)) {
  1122.             t_property_desc(spbuf, t_river_chance, NULL);
  1123.             tprintf(buf, "  Chance to be river source: %s.\n", spbuf);
  1124.             if (g_river_sink_terrain() != NONTTYPE)
  1125.               tprintf(buf, "  Sink is %s.\n", t_type_name(g_river_sink_terrain()));
  1126.             else
  1127.               tprintf(buf, "  No special sink terrain type.\n");
  1128.         }
  1129.         break;
  1130.           case K_MAKE_ROADS:
  1131.         tprintf(buf, "\nRoads:");
  1132.         describe_synth_run(buf, methkey);
  1133.         tprintf(buf, "\n");
  1134.         tprintf(buf, "  Chance to run:\n");
  1135.         for_all_unit_types(u) {
  1136.             if (uu_table_row_not_default(u, uu_road_chance, 0)) {
  1137.             tprintf(buf, "  %s:", u_type_name(u));
  1138.             uu_table_row_desc(spbuf, u, uu_road_chance, NULL);
  1139.             tprintf(buf, " %s\n", spbuf);
  1140.             }
  1141.  
  1142.         }
  1143.         /* (should include routing terrain preferences) */
  1144.         break;
  1145.           case K_MAKE_WEATHER:
  1146.         tprintf(buf, "\nWeather:");
  1147.         describe_synth_run(buf, methkey);
  1148.         tprintf(buf, "\n");
  1149.         break;
  1150.           case K_NAME_GEOGRAPHICAL_FEATURES:
  1151.         tprintf(buf, "\nNames for geographical features:");
  1152.         describe_synth_run(buf, methkey);
  1153.         tprintf(buf, "\n");
  1154.         break;
  1155.           case K_NAME_UNITS_RANDOMLY:
  1156.         tprintf(buf, "\nNames for units:");
  1157.         describe_synth_run(buf, methkey);
  1158.         tprintf(buf, "\n");
  1159.         break;
  1160.           default:
  1161.         tprintf(buf, "\n%s:", c_string(method));
  1162.         describe_synth_run(buf, methkey);
  1163.         tprintf(buf, "\n");
  1164.         break;
  1165.         }
  1166.     } else if (consp(method)) {
  1167.     }
  1168.     }
  1169. }
  1170.  
  1171. static void
  1172. describe_synth_run(buf, methkey)
  1173. char *buf;
  1174. int methkey;
  1175. {
  1176.     int calls, runs;
  1177.   
  1178.     if (get_synth_method_uses(methkey, &calls, &runs)) {
  1179.     if (calls > 0) {
  1180.         if (calls == 1 && runs == 1) {
  1181.         tprintf(buf, " (was run)");
  1182.         } else if (calls == 1 && runs == 0) {
  1183.         tprintf(buf, " (was not run)");
  1184.         } else {
  1185.         tprintf(buf, " (was called %d times, was run %d times)",
  1186.             calls, runs);
  1187.         }
  1188.     } else {
  1189.         tprintf(buf, " (not attempted)");
  1190.     }
  1191.     } else {
  1192.     tprintf(buf, " (???)");
  1193.     }
  1194. }
  1195.  
  1196. /* This describes a command (from cmd.def et al) in a way that all
  1197.    interfaces can use. */
  1198.  
  1199. void
  1200. describe_command (ch, name, help, onechar, buf)
  1201. int ch, onechar;
  1202. char *name, *help, *buf;
  1203. {
  1204.     if (onechar && ch != '\0') {
  1205.     if (ch < ' ' || ch > '~') { 
  1206.         sprintf(buf+strlen(buf), "'^%c' ", (ch ^ 0x40));
  1207.     } else {
  1208.         sprintf(buf+strlen(buf), " '%c' ", ch);
  1209.     }
  1210.     } else if (!onechar && ch == '\0') {
  1211.     strcat(buf, "\"");
  1212.     strcat(buf, name);
  1213.     strcat(buf, "\"");
  1214.     } else
  1215.       return;
  1216.     strcat(buf, " ");
  1217.     strcat(buf, help);
  1218.     strcat(buf, "\n");
  1219. }
  1220.  
  1221. int
  1222. u_property_not_default(fn, dflt)
  1223. int (*fn) PARAMS ((int i));
  1224. int dflt;
  1225. {
  1226.     int u, val;
  1227.  
  1228.     for_all_unit_types(u) {
  1229.     val = (*fn)(u);
  1230.     if (val != dflt)
  1231.       return TRUE;
  1232.     }
  1233.     return FALSE;
  1234. }
  1235.  
  1236. int
  1237. t_property_not_default(fn, dflt)
  1238. int (*fn) PARAMS ((int i));
  1239. int dflt;
  1240. {
  1241.     int t, val;
  1242.  
  1243.     for_all_terrain_types(t) {
  1244.     val = (*fn)(t);
  1245.     if (val != dflt)
  1246.       return TRUE;
  1247.     }
  1248.     return FALSE;
  1249. }
  1250.  
  1251. int
  1252. uu_table_row_not_default(u, fn, dflt)
  1253. int u, dflt;
  1254. int (*fn) PARAMS ((int i, int j));
  1255. {
  1256.     int u2, val2;
  1257.  
  1258.     for_all_unit_types(u2) {
  1259.     val2 = (*fn)(u, u2);
  1260.     if (val2 != dflt)
  1261.       return TRUE;
  1262.     }
  1263.     return FALSE;
  1264. }
  1265.  
  1266. int
  1267. ut_table_row_not_default(u, fn, dflt)
  1268. int u, dflt;
  1269. int (*fn) PARAMS ((int i, int j));
  1270. {
  1271.     int t, val2;
  1272.  
  1273.     for_all_terrain_types(t) {
  1274.     val2 = (*fn)(u, t);
  1275.     if (val2 != dflt)
  1276.       return TRUE;
  1277.     }
  1278.     return FALSE;
  1279. }
  1280.  
  1281. int
  1282. um_table_row_not_default(u, fn, dflt)
  1283. int u, dflt;
  1284. int (*fn) PARAMS ((int i, int j));
  1285. {
  1286.     int m, val2;
  1287.  
  1288.     for_all_material_types(m) {
  1289.     val2 = (*fn)(u, m);
  1290.     if (val2 != dflt)
  1291.       return TRUE;
  1292.     }
  1293.     return FALSE;
  1294. }
  1295.  
  1296. struct histo {
  1297.     int val, num;
  1298. };
  1299.  
  1300. /* This compare will sort histogram entries in *reverse* order
  1301.    (most common values first). */
  1302.  
  1303. static int
  1304. histogram_compare(h1, h2)
  1305. CONST void *h1, *h2;
  1306. {
  1307.     if (((struct histo *) h2)->num != ((struct histo *) h1)->num) {
  1308.         return ((struct histo *) h2)->num - ((struct histo *) h1)->num;
  1309.     } else {
  1310.         return ((struct histo *) h2)->val - ((struct histo *) h1)->val;
  1311.     }
  1312. }
  1313.  
  1314. void
  1315. u_property_desc(buf, fn, formatter)
  1316. char *buf;
  1317. int (*fn) PARAMS ((int i));
  1318. void (*formatter) PARAMS ((char *buf, int val));
  1319. {
  1320.     int val, u, val2, constant = TRUE, found;
  1321.     int i, numentries, first;
  1322.     struct histo histogram[MAXUTYPES];
  1323.  
  1324.     /* Compute a histogram of all the values for the given property. */
  1325.     numentries = 0;
  1326.     val = (*fn)(0);
  1327.     histogram[numentries].val = val;
  1328.     histogram[numentries].num = 1;
  1329.     ++numentries;
  1330.     for_all_unit_types(u) {
  1331.     val2 = (*fn)(u);
  1332.     if (val2 == val) {
  1333.         ++(histogram[0].num);
  1334.     } else {
  1335.         constant = FALSE;
  1336.         found = FALSE;
  1337.         for (i = 1; i < numentries; ++i) {
  1338.         if (val2 == histogram[i].val) {
  1339.             ++(histogram[i].num);
  1340.             found = TRUE;
  1341.             break;
  1342.         }
  1343.         }
  1344.         if (!found) {
  1345.         histogram[numentries].val = val2;
  1346.         histogram[numentries].num = 1;
  1347.         ++numentries;
  1348.         }
  1349.     }
  1350.     }
  1351.     /* The constant table/row case is easily disposed of. */
  1352.     if (constant) {
  1353.     if (formatter == NULL) {
  1354.         sprintf(buf, "%d for all types", val);
  1355.         } else {
  1356.         (*formatter)(buf, val);
  1357.         strcat(buf, " for all types");
  1358.         }
  1359.         return;
  1360.     }
  1361.     /* Not a constant row; sort the histogram and compose a description. */
  1362.     qsort(histogram, numentries, sizeof(struct histo), histogram_compare);
  1363.     if (histogram[0].num * 2 >= numutypes) {
  1364.         if (formatter == NULL) {
  1365.         sprintf(buf, "%d by default", histogram[0].val);
  1366.         } else {
  1367.         char subbuf[40];
  1368.  
  1369.         (*formatter)(subbuf, histogram[0].val);
  1370.         sprintf(buf, "%s by default", subbuf);
  1371.         }
  1372.         i = 1;
  1373.     } else {
  1374.     buf[0] = '\0';
  1375.         i = 0;
  1376.     }
  1377.     for (; i < numentries; ++i) {
  1378.     if (i > 0)
  1379.       tnprintf(buf, BUFSIZE, ", ");
  1380.     if (formatter == NULL) {
  1381.         tnprintf(buf, BUFSIZE, "%d for ", histogram[i].val);
  1382.     } else {
  1383.         char subbuf[40];
  1384.  
  1385.         (*formatter)(subbuf, histogram[i].val);
  1386.         tnprintf(buf, BUFSIZE, "%s for ", subbuf);
  1387.     }
  1388.     first = TRUE;
  1389.     for_all_unit_types(u) {
  1390.         if ((*fn)(u) == histogram[i].val) {
  1391.         if (!first)
  1392.           tnprintf(buf, BUFSIZE, ",");
  1393.         else
  1394.           first = FALSE;
  1395.         tnprintf(buf, BUFSIZE, "%s", u_type_name(u));
  1396.         }
  1397.     }
  1398.     }
  1399. }
  1400.  
  1401. void
  1402. t_property_desc(buf, fn, formatter)
  1403. char *buf;
  1404. int (*fn) PARAMS ((int i));
  1405. void (*formatter) PARAMS ((char *buf, int val));
  1406. {
  1407.     int val, t, val2, constant = TRUE, found;
  1408.     int i, numentries, first;
  1409.     struct histo histogram[MAXUTYPES];
  1410.  
  1411.     /* Compute a histogram of all the values for the given property. */
  1412.     numentries = 0;
  1413.     val = (*fn)(0);
  1414.     histogram[numentries].val = val;
  1415.     histogram[numentries].num = 1;
  1416.     ++numentries;
  1417.     for_all_terrain_types(t) {
  1418.     val2 = (*fn)(t);
  1419.     if (val2 == val) {
  1420.         ++(histogram[0].num);
  1421.     } else {
  1422.         constant = FALSE;
  1423.         found = FALSE;
  1424.         for (i = 1; i < numentries; ++i) {
  1425.         if (val2 == histogram[i].val) {
  1426.             ++(histogram[i].num);
  1427.             found = TRUE;
  1428.             break;
  1429.         }
  1430.         }
  1431.         if (!found) {
  1432.         histogram[numentries].val = val2;
  1433.         histogram[numentries].num = 1;
  1434.         ++numentries;
  1435.         }
  1436.     }
  1437.     }
  1438.     /* The constant table/row case is easily disposed of. */
  1439.     if (constant) {
  1440.     if (formatter == NULL) {
  1441.         sprintf(buf, "%d for all types", val);
  1442.         } else {
  1443.         (*formatter)(buf, val);
  1444.         strcat(buf, " for all types");
  1445.         }
  1446.         return;
  1447.     }
  1448.     /* Not a constant row; sort the histogram and compose a description. */
  1449.     qsort(histogram, numentries, sizeof(struct histo), histogram_compare);
  1450.     if (histogram[0].num * 2 >= numttypes) {
  1451.         if (formatter == NULL) {
  1452.         sprintf(buf, "%d by default", histogram[0].val);
  1453.         } else {
  1454.         char subbuf[40];
  1455.  
  1456.         (*formatter)(subbuf, histogram[0].val);
  1457.         sprintf(buf, "%s by default", subbuf);
  1458.         }
  1459.         i = 1;
  1460.     } else {
  1461.     buf[0] = '\0';
  1462.         i = 0;
  1463.     }
  1464.     for (; i < numentries; ++i) {
  1465.     if (i > 0)
  1466.       tnprintf(buf, BUFSIZE, ", ");
  1467.     if (formatter == NULL) {
  1468.         tnprintf(buf, BUFSIZE, "%d for ", histogram[i].val);
  1469.     } else {
  1470.         char subbuf[40];
  1471.  
  1472.         (*formatter)(subbuf, histogram[i].val);
  1473.         tnprintf(buf, BUFSIZE, "%s for ", subbuf);
  1474.     }
  1475.     first = TRUE;
  1476.     for_all_terrain_types(t) {
  1477.         if ((*fn)(t) == histogram[i].val) {
  1478.         if (!first)
  1479.           tnprintf(buf, BUFSIZE, ",");
  1480.         else
  1481.           first = FALSE;
  1482.         tnprintf(buf, BUFSIZE, "%s", t_type_name(t));
  1483.         }
  1484.     }
  1485.     }
  1486. }
  1487.  
  1488. void
  1489. uu_table_row_desc(buf, u, fn, formatter)
  1490. char *buf;
  1491. int u;
  1492. int (*fn) PARAMS ((int i, int j));
  1493. void (*formatter) PARAMS ((char *buf, int val));
  1494. {
  1495.     int val = (*fn)(u, 0), val2, u2, constant = TRUE, found;
  1496.     int i, numentries, first;
  1497.     struct histo histogram[MAXUTYPES];
  1498.  
  1499.     /* Compute a histogram of all the values in the row of the table. */
  1500.     numentries = 0;
  1501.     histogram[numentries].val = val;
  1502.     histogram[numentries].num = 1;
  1503.     ++numentries;
  1504.     for_all_unit_types(u2) {
  1505.     val2 = (*fn)(u, u2);
  1506.     if (val2 == val) {
  1507.         ++(histogram[0].num);
  1508.     } else {
  1509.         constant = FALSE;
  1510.         found = FALSE;
  1511.         for (i = 1; i < numentries; ++i) {
  1512.         if (val2 == histogram[i].val) {
  1513.             ++(histogram[i].num);
  1514.             found = TRUE;
  1515.             break;
  1516.         }
  1517.         }
  1518.         if (!found) {
  1519.         histogram[numentries].val = val2;
  1520.         histogram[numentries].num = 1;
  1521.         ++numentries;
  1522.         }
  1523.     }
  1524.     }
  1525.     /* The constant table/row case is easily disposed of. */
  1526.     if (constant) {
  1527.     if (formatter == NULL) {
  1528.         sprintf(buf, "%d for all types", val);
  1529.         } else {
  1530.         (*formatter)(buf, val);
  1531.         strcat(buf, " for all types");
  1532.         }
  1533.         return;
  1534.     }
  1535.     /* Not a constant row; sort the histogram and compose a description. */
  1536.     qsort(histogram, numentries, sizeof(struct histo), histogram_compare);
  1537.     if (histogram[0].num * 2 >= numutypes) {
  1538.         if (formatter == NULL) {
  1539.         sprintf(buf, "%d by default", histogram[0].val);
  1540.         } else {
  1541.         char subbuf[40];
  1542.  
  1543.         (*formatter)(subbuf, histogram[0].val);
  1544.         sprintf(buf, "%s by default", subbuf);
  1545.         }
  1546.         i = 1;
  1547.     } else {
  1548.     buf[0] = '\0';
  1549.         i = 0;
  1550.     }
  1551.     for (; i < numentries; ++i) {
  1552.     if (i > 0) tnprintf(buf, BUFSIZE, ", ");
  1553.     if (formatter == NULL) {
  1554.         tnprintf(buf, BUFSIZE, "%d vs ", histogram[i].val);
  1555.     } else {
  1556.         char subbuf[40];
  1557.  
  1558.         (*formatter)(subbuf, histogram[i].val);
  1559.         tnprintf(buf, BUFSIZE, "%s vs ", subbuf);
  1560.     }
  1561.     first = TRUE;
  1562.     for_all_unit_types(u2) {
  1563.         if ((*fn)(u, u2) == histogram[i].val) {
  1564.         if (!first) tnprintf(buf, BUFSIZE, ",");  else first = FALSE;
  1565.         tnprintf(buf, BUFSIZE, "%s", u_type_name(u2));
  1566.         }
  1567.     }
  1568.     }
  1569. }
  1570.  
  1571. void
  1572. ut_table_row_desc(buf, u, fn, formatter)
  1573. char *buf;
  1574. int u;
  1575. int (*fn) PARAMS ((int i, int j));
  1576. void (*formatter) PARAMS ((char *buf, int val));
  1577. {
  1578.     int val = (*fn)(u, 0), val2, t, constant = TRUE, found;
  1579.     int i, numentries, first;
  1580.     struct histo histogram[MAXUTYPES];
  1581.  
  1582.     /* Compute a histogram of all the values in the row of the table. */
  1583.     numentries = 0;
  1584.     histogram[numentries].val = val;
  1585.     histogram[numentries].num = 1;
  1586.     ++numentries;
  1587.     for_all_terrain_types(t) {
  1588.     val2 = (*fn)(u, t);
  1589.     if (val2 == val) {
  1590.         ++(histogram[0].num);
  1591.     } else {
  1592.         constant = FALSE;
  1593.         found = FALSE;
  1594.         for (i = 1; i < numentries; ++i) {
  1595.         if (val2 == histogram[i].val) {
  1596.             ++(histogram[i].num);
  1597.             found = TRUE;
  1598.             break;
  1599.         }
  1600.         }
  1601.         if (!found) {
  1602.         histogram[numentries].val = val2;
  1603.         histogram[numentries].num = 1;
  1604.         ++numentries;
  1605.         }
  1606.     }
  1607.     }
  1608.     /* The constant table/row case is easily disposed of. */
  1609.     if (constant) {
  1610.     if (formatter == NULL) {
  1611.         sprintf(buf, "%d for all types", val);
  1612.         } else {
  1613.         (*formatter)(buf, val);
  1614.         strcat(buf, " for all types");
  1615.         }
  1616.         return;
  1617.     }
  1618.     /* Not a constant row; sort the histogram and compose a description. */
  1619.     qsort(histogram, numentries, sizeof(struct histo), histogram_compare);
  1620.     if (histogram[0].num * 2 >= numttypes) {
  1621.         if (formatter == NULL) {
  1622.         sprintf(buf, "%d by default", histogram[0].val);
  1623.         } else {
  1624.         char subbuf[40];
  1625.  
  1626.         (*formatter)(subbuf, histogram[0].val);
  1627.         sprintf(buf, "%s by default", subbuf);
  1628.         }
  1629.         i = 1;
  1630.     } else {
  1631.     buf[0] = '\0';
  1632.         i = 0;
  1633.     }
  1634.     for (; i < numentries; ++i) {
  1635.     if (i > 0)
  1636.       tnprintf(buf, BUFSIZE, ", ");
  1637.     if (formatter == NULL) {
  1638.         tnprintf(buf, BUFSIZE, "%d vs ", histogram[i].val);
  1639.     } else {
  1640.         char subbuf[40];
  1641.  
  1642.         (*formatter)(subbuf, histogram[i].val);
  1643.         tnprintf(buf, BUFSIZE, "%s vs ", subbuf);
  1644.     }
  1645.     first = TRUE;
  1646.     for_all_terrain_types(t) {
  1647.         if ((*fn)(u, t) == histogram[i].val) {
  1648.         if (!first)
  1649.           tnprintf(buf, BUFSIZE, ",");
  1650.         else
  1651.           first = FALSE;
  1652.         tnprintf(buf, BUFSIZE, "%s", t_type_name(t));
  1653.         }
  1654.     }
  1655.     }
  1656. }
  1657.  
  1658. void
  1659. um_table_row_desc(buf, u, fn, formatter)
  1660. char *buf;
  1661. int u;
  1662. int (*fn) PARAMS ((int i, int j));
  1663. void (*formatter) PARAMS ((char *buf, int val));
  1664. {
  1665.     int val = (*fn)(u, 0), val2, m, constant = TRUE, found;
  1666.     int i, numentries, first;
  1667.     struct histo histogram[MAXUTYPES];
  1668.  
  1669.     /* Compute a histogram of all the values in the row of the table. */
  1670.     numentries = 0;
  1671.     histogram[numentries].val = val;
  1672.     histogram[numentries].num = 1;
  1673.     ++numentries;
  1674.     for_all_material_types(m) {
  1675.     val2 = (*fn)(u, m);
  1676.     if (val2 == val) {
  1677.         ++(histogram[0].num);
  1678.     } else {
  1679.         constant = FALSE;
  1680.         found = FALSE;
  1681.         for (i = 1; i < numentries; ++i) {
  1682.         if (val2 == histogram[i].val) {
  1683.             ++(histogram[i].num);
  1684.             found = TRUE;
  1685.             break;
  1686.         }
  1687.         }
  1688.         if (!found) {
  1689.         histogram[numentries].val = val2;
  1690.         histogram[numentries].num = 1;
  1691.         ++numentries;
  1692.         }
  1693.     }
  1694.     }
  1695.     /* The constant table/row case is easily disposed of. */
  1696.     if (constant) {
  1697.     if (formatter == NULL) {
  1698.         sprintf(buf, "%d for all types", val);
  1699.         } else {
  1700.         (*formatter)(buf, val);
  1701.         strcat(buf, " for all types");
  1702.         }
  1703.         return;
  1704.     }
  1705.     /* Not a constant row; sort the histogram and compose a description. */
  1706.     qsort(histogram, numentries, sizeof(struct histo), histogram_compare);
  1707.     if (histogram[0].num * 2 >= nummtypes) {
  1708.         if (formatter == NULL) {
  1709.         sprintf(buf, "%d by default", histogram[0].val);
  1710.         } else {
  1711.         char subbuf[40];
  1712.  
  1713.         (*formatter)(subbuf, histogram[0].val);
  1714.         sprintf(buf, "%s by default", subbuf);
  1715.         }
  1716.         i = 1;
  1717.     } else {
  1718.     buf[0] = '\0';
  1719.         i = 0;
  1720.     }
  1721.     for (; i < numentries; ++i) {
  1722.     if (i > 0)
  1723.       tnprintf(buf, BUFSIZE, ", ");
  1724.     if (formatter == NULL) {
  1725.         tnprintf(buf, BUFSIZE, "%d vs ", histogram[i].val);
  1726.     } else {
  1727.         char subbuf[40];
  1728.  
  1729.         (*formatter)(subbuf, histogram[i].val);
  1730.         tnprintf(buf, BUFSIZE, "%s vs ", subbuf);
  1731.     }
  1732.     first = TRUE;
  1733.     for_all_material_types(m) {
  1734.         if ((*fn)(u, m) == histogram[i].val) {
  1735.         if (!first)
  1736.           tnprintf(buf, BUFSIZE, ",");
  1737.         else
  1738.           first = FALSE;
  1739.         tnprintf(buf, BUFSIZE, "%s", m_type_name(m));
  1740.         }
  1741.     }
  1742.     }
  1743. }
  1744.  
  1745. /* A simple table-printing utility. Blanks out default values so they don't
  1746.    clutter the table. */
  1747. /* (not currently used anywhere?) */
  1748.  
  1749. void
  1750. append_number(buf, value, dflt)
  1751. char *buf;
  1752. int value, dflt;
  1753. {
  1754.     if (value != dflt) {
  1755.     sprintf(tmpbuf, "%5d ", value);
  1756.     tprintf(buf, "%s", tmpbuf);
  1757.     } else {
  1758.     tprintf(buf, "      ");
  1759.     }
  1760. }
  1761.  
  1762. void
  1763. append_help_phrase(buf, phrase)
  1764. char *buf, *phrase;
  1765. {
  1766.     if (empty_string(phrase))
  1767.       return;
  1768.     tprintf(buf, "-- ");
  1769.     tprintf(buf, "%s", phrase);
  1770.     tprintf(buf, " --\n");
  1771. }
  1772.  
  1773. void
  1774. append_notes(buf, notes)
  1775. char *buf;
  1776. Obj *notes;
  1777. {
  1778.     char *notestr;
  1779.     Obj *rest;
  1780.  
  1781.     if (stringp(notes)) {
  1782.     notestr = c_string(notes);
  1783.     if (strlen(notestr) > 0) { 
  1784.         strcat(buf, notestr);
  1785.         strcat(buf, " ");
  1786.     } else {
  1787.         strcat(buf, "\n");
  1788.     }
  1789.     } else if (consp(notes)) {
  1790.     for (rest = notes; rest != lispnil; rest = cdr(rest)) {
  1791.         append_notes(buf, car(rest));
  1792.     }
  1793.     } else {
  1794.     /* error? */
  1795.     }
  1796. }
  1797.  
  1798. void
  1799. notify_instructions()
  1800. {
  1801.     Obj *instructions = mainmodule->instructions, *rest;
  1802.  
  1803.     if (instructions != lispnil) {
  1804.     if (stringp(instructions)) {
  1805.         notify_all("%s", c_string(instructions));
  1806.     } else if (consp(instructions)) {
  1807.         for (rest = instructions; rest != lispnil; rest = cdr(rest)) {
  1808.         if (stringp(car(rest))) {
  1809.             notify_all("%s", c_string(car(rest)));
  1810.         } else {
  1811.             /* (should probably warn about this case too) */
  1812.         }
  1813.         }
  1814.     } else {
  1815.         run_warning("Instructions are of wrong type");
  1816.     }
  1817.     } else {
  1818.     notify_all("(no instructions supplied)");
  1819.     }
  1820. }
  1821.  
  1822. /* Print the news file onto the console if there is anything to print. */
  1823.  
  1824. void
  1825. print_any_news()
  1826. {
  1827.     FILE *fp;
  1828.  
  1829.     fp = open_library_file(news_filename());
  1830.     if (fp != NULL) {
  1831.     printf("\n                              XCONQ NEWS\n\n");
  1832.     while (fgets(spbuf, BUFSIZE-1, fp) != NULL) {
  1833.         fputs(spbuf, stdout);
  1834.     }
  1835.     /* Add another blank line, to separate from init printouts. */
  1836.     printf("\n");
  1837.     fclose(fp);
  1838.     }
  1839. }
  1840.  
  1841. /* Generate a readable description of the game (design) being played. */
  1842. /* This works by writing out appropriate help nodes, along with some
  1843.    indexing material.  This does *not* do interface-specific help,
  1844.    such as commands. */
  1845.  
  1846. void
  1847. print_game_description_to_file(fp)
  1848. FILE *fp;
  1849. {
  1850.     HelpNode *node;
  1851.  
  1852.     /* (need to work on which nodes to dump out) */
  1853.     for (node = first_help_node; node != first_help_node; node = node->next) {
  1854.     get_help_text(node);
  1855.     if (node->text != NULL) {
  1856.         fprintf(fp, "\014\n%s\n", node->key);
  1857.         fprintf(fp, "%s\n", node->text);
  1858.     }
  1859.     }
  1860. }
  1861.